package com.rc.evcharger.common.base;

import com.mongodb.MongoException;
import com.mongodb.client.MongoDatabase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.DbCallback;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 支持自动重连
 * https://www.baeldung.com/queries-in-spring-data-mongodb
 * Created by sven on 2019/6/24
 */
public abstract class BaseMongoDaoImpl<T extends BaseMongoEntity> implements BaseMongoDao<T> {
    protected abstract Class<T> getEntityClass();

    @Autowired
    protected MongoTemplate mongoTemplate;

    //@Override
    //public void insert(T entity) {
    //    mongoTemplate.insert(entity);
    //}

    @Override
    public void insert(T entity, String collectionName) {
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.insert(entity, collectionName);
            }
        });

        //mongoTemplate.insert(entity, collectionName);
    }

    @Override
    public void save(T entity, String collectionName) {
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.save(entity, collectionName);
            }
        });

        //mongoTemplate.save(entity, collectionName);
    }

    @Override
    public void update(Query query, Update update, String collectionName) {
        //updateMulti 如果根据查询条件找到对应的多条记录是，全部更新
        //updateFirst 更改符合条件的第一个
        //upsert 顾名思义 update+insert 如果根据条件没有对应的数据,则执行插入
        //findAndModify 查询然后更新

        //Map<String, Object> map = null;
        //try {
        //    map = parseEntity(entity);
        //} catch (Exception e) {
        //    e.printStackTrace();
        //}
        //String id = null;
        //Object value = null;
        //Update update = new Update();
        //if (map != null && map.size() > 0) {
        //    for (String key : map.keySet()) {
        //        if (key.startsWith("{")) {
        //            id = key.substring(key.indexOf("{") + 1, key.indexOf("}"));
        //            value = map.get(key);
        //        } else {
        //            update.set(key, map.get(key));
        //        }
        //    }
        //}

        //mongoTemplate.updateFirst(new Query().addCriteria(Criteria.where(id).is(value)), update, getEntityClass());
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.updateMulti(query, update, getEntityClass(), collectionName);
            }
        });

        //mongoTemplate.updateMulti(query, update, getEntityClass(), collectionName);
    }

    @Override
    public void delete(Query query, String collectionName) {
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findAndRemove(query, getEntityClass(), collectionName);
            }
        });

        //mongoTemplate.findAndRemove(query, getEntityClass(), collectionName);
    }

    @Override
    public T findById(String id, String collectionName) {
        return mongoTemplate.execute(new DbCallback<T>() {
            public T doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findById(id, getEntityClass(), collectionName);
            }
        });

        //return mongoTemplate.findById(id, getEntityClass(), collectionName);
    }

    @Override
    public T findOne(Query query, String collectionName) {
        return mongoTemplate.execute(new DbCallback<T>() {
            public T doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findOne(query, getEntityClass(), collectionName);
            }
        });

        //return mongoTemplate.findOne(query, getEntityClass(), collectionName);
    }

    @Override
    public List<T> find(Query query, String collectionName) {
        return mongoTemplate.execute(new DbCallback<List<T>>() {
            public List<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.find(query, getEntityClass(), collectionName);
            }
        });

        //return mongoTemplate.find(query, getEntityClass(), collectionName);
    }

    @Override
    public List<T> find(Query query, Sort sort, String collectionName) {
        //query.with(new Sort(new Order(Direction.DESC,"id")));
        //query.with(new Sort(Sort.Direction.DESC,"TIME1"));
        query.with(sort);

        return mongoTemplate.execute(new DbCallback<List<T>>() {
            public List<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.find(query, getEntityClass(), collectionName);
            }
        });

        //return mongoTemplate.find(query, getEntityClass(), collectionName);
    }

    @Override
    public PageImpl<T> findPage(Query query, Sort sort, int pageIndex, int pageSize, String collectionName) {
        final Pageable pageableRequest = PageRequest.of(pageIndex, pageSize);
        query.with(sort);
        query.with(pageableRequest);

        return mongoTemplate.execute(new DbCallback<PageImpl<T>>() {
            public PageImpl<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                // 查询总数
                int count = (int) mongoTemplate.count(query, getEntityClass(), collectionName);

                List<T> list = new ArrayList<T>();
                if (count > 0) {
                    list = mongoTemplate.find(query, getEntityClass(), collectionName);
                }

                return (PageImpl<T>) PageableExecutionUtils.getPage(list, pageableRequest, () -> count);
            }
        });

        // 查询总数
        //int count = (int) mongoTemplate.count(query, getEntityClass(), collectionName);
        //
        //List<T> list = mongoTemplate.find(query, getEntityClass(), collectionName);
        ////return (PageImpl<T>) PageableExecutionUtils.getPage(list, pageableRequest, () -> 0);
        //return (PageImpl<T>) PageableExecutionUtils.getPage(list, pageableRequest, () -> count);
    }

    @Override
    public List<T> findAll(Sort sort, String collectionName) {
        return mongoTemplate.execute(new DbCallback<List<T>>() {
            public List<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findAll(getEntityClass(), collectionName);
            }
        });

        //return mongoTemplate.findAll(getEntityClass(), collectionName);
    }

    @Override
    public Long count(Query query, String collectionName) {
        return mongoTemplate.execute(new DbCallback<Long>() {
            public Long doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.count(query, getEntityClass(), collectionName);
            }
        });

        //Long count = mongoTemplate.count(query, getEntityClass(), collectionName);
        //return count.intValue();
    }

    //@SuppressWarnings("deprecation")
    //@Override
    //public List<T> findAll(String order) {
    //    List<Order> orderlist = parseOrder(order);
    //    if (orderlist == null || orderlist.size() == 0) {
    //        return findAll();
    //    }
    //    return mongoTemplate.find(new Query().with(new Sort(orderlist)), getEntityClass());
    //}
    //
    //@Override
    //public List<T> findByProp(String propName, Object value) {
    //    return findByProp(propName, value, null);
    //}
    //
    //@SuppressWarnings("deprecation")
    //@Override
    //public List<T> findByProp(String propName, Object value, String order) {
    //    Query query = new Query();
    //    query.addCriteria(Criteria.where(propName).is(value));
    //    List<Order> orderlist = parseOrder(order);
    //    if (orderlist != null && orderlist.size() > 0) {
    //        query.with(new Sort(orderlist));
    //    }
    //    return null;
    //}
    //
    //@Override
    //public List<T> findByProps(String[] propName, Object[] values) {
    //    return findByProps(propName, values, null);
    //}
    //
    //@Override
    //public List<T> findByProps(String[] propName, Object[] values, String order) {
    //    Query query = createQuery(propName, values, order);
    //    return mongoTemplate.find(query, getEntityClass());
    //}
    //
    //@Override
    //public T uniqueByProp(String propName, Object value) {
    //    return mongoTemplate.findOne(new Query().addCriteria(Criteria.where(propName).is(value)), getEntityClass());
    //}
    //
    //@Override
    //public T uniqueByProps(String[] propName, Object[] values) {
    //    Query query = createQuery(propName, values, null);
    //    return mongoTemplate.findOne(query, getEntityClass());
    //}

    //@Override
    //public int countByCondition(String[] params, Object[] values) {
    //    Query query = createQuery(params, values, null);
    //    Long count = mongoTemplate.count(query, getEntityClass());
    //    return count.intValue();
    //}

    //protected Map<String, Object> parseEntity(T t) throws Exception {
    //    Map<String, Object> map = new HashMap<>();
    //    String id = "";
    //    Field[] declaredFields = getEntityClass().getDeclaredFields();
    //    for (Field field : declaredFields) {
    //        if (field.isAnnotationPresent(Id.class)) {
    //            field.setAccessible(true);
    //            map.put("{" + field.getName() + "}", field.get(t));
    //            id = field.getName();
    //            break;
    //        }
    //    }
    //    Method[] declaredMethods = getEntityClass().getDeclaredMethods();
    //    if (declaredFields != null && declaredFields.length > 0) {
    //        for (Method method : declaredMethods) {
    //            if (method.getName().startsWith("get") && method.getModifiers() == Modifier.PUBLIC) {
    //                String fieldName = parse2FieldName(method.getName());
    //                if (!fieldName.equals(id)) {
    //                    map.put(fieldName, method.invoke(t));
    //                }
    //            }
    //        }
    //    }
    //    return map;
    //}
    //
    //private String parse2FieldName(String method) {
    //    String name = method.replace("get", "");
    //    name = name.substring(0, 1).toLowerCase() + name.substring(1);
    //    return name;
    //}
    //
    //@SuppressWarnings("deprecation")
    //public Query createQuery(String[] propName, Object[] values, String order) {
    //    Query query = new Query();
    //    //where
    //    if (propName != null && values != null) {
    //        for (int i = 0; i < propName.length; i++) {
    //            query.addCriteria(Criteria.where(propName[i]).is(values[i]));
    //        }
    //    }
    //    List<Order> orderlist = parseOrder(order);
    //    if (orderlist != null && orderlist.size() > 0) {
    //        query.with(new Sort(orderlist));
    //    }
    //    return query;
    //}
    //
    //public List<Order> parseOrder(String order) {
    //    List<Order> list = null;
    //    if (order != null && !"".equals(order)) {
    //        list = new ArrayList<>();
    //        String[] fields = order.split(",");
    //        Order o = null;
    //        String[] items = null;
    //        for (int i = 0; i < fields.length; i++) {
    //            if (fields[i] == null) {
    //                continue;
    //            }
    //            items = fields[i].split(" ");
    //            if (items.length == 1) {
    //                o = new Order(Direction.ASC, items[0]);
    //            } else if (items.length == 2) {
    //                o = new Order("desc".equalsIgnoreCase(items[1]) ? Direction.DESC : Direction.ASC, items[0]);
    //            } else {
    //                throw new RuntimeException("order field parse error");
    //            }
    //            list.add(o);
    //        }
    //    }
    //    return list;
    //}
}
